package cn.snmp.mutual.uk.stack;

import java.text.*;
import java.net.*;
import cn.snmp.mutual.uk.util.SnmpUtilities;

import java.io.*;
import java.util.*;

public class AsnOctets extends AsnObject {
    private static final String version_id = "@(#)$Id: AsnOctets.java,v 3.39 2006/03/23 14:54:10 birgit Exp $ Copyright Westhawk Ltd";
    public static String HEX_PREFIX;
    public static SimpleDateFormat CALFORMAT;
    public static AsnOctetsPrintableFace printableObject;
    byte[] value;
    private int hash;

    public AsnOctets(final byte[] s) throws IllegalArgumentException {
        this(s, (byte) 4);
    }

    public AsnOctets(final byte[] s, final byte t) throws IllegalArgumentException {
        this.hash = 0;
        this.value = s;
        this.type = t;
        if (this.value == null) {
            throw new IllegalArgumentException("Value is null");
        }
    }

    public AsnOctets(final char[] s) {
        this.hash = 0;
        this.value = new byte[s.length];
        this.type = 4;
        for (int idx = 0; idx < s.length; ++idx) {
            this.value[idx] = (byte) s[idx];
        }
    }

    public AsnOctets(final String s) {
        this(s.toCharArray());
    }

    public AsnOctets(final InetAddress iad) throws IllegalArgumentException {
        this(iad.getAddress(), (byte) 4);
        if (iad instanceof Inet4Address) {
            this.type = 64;
        }
    }

    public AsnOctets(final Inet4Address iad, final byte t) throws IllegalArgumentException {
        this(iad.getAddress(), t);
    }

    public AsnOctets(final Calendar cal) {
        this.hash = 0;
        this.value = new byte[11];
        this.type = 4;
        final int year = cal.get(1);
        final int month = cal.get(2) + 1;
        final int day = cal.get(5);
        final int hour = cal.get(11);
        final int min = cal.get(12);
        final int sec = cal.get(13);
        final int msec = cal.get(14);
        int msecGMT = cal.get(15);
        this.value[0] = (byte) (year / 256 % 256);
        this.value[1] = (byte) (year % 256);
        this.value[2] = (byte) (month & 0xFF);
        this.value[3] = (byte) (day & 0xFF);
        this.value[4] = (byte) (hour & 0xFF);
        this.value[5] = (byte) (min & 0xFF);
        this.value[6] = (byte) (sec & 0xFF);
        this.value[7] = (byte) (msec / 100 & 0xFF);
        char dir = '\0';
        if (msecGMT < 0) {
            dir = '-';
            msecGMT *= -1;
        } else {
            dir = '+';
        }
        this.value[8] = (byte) dir;
        if (msecGMT == 0) {
            this.value[9] = 0;
            this.value[10] = 0;
        } else {
            int minGMT = (int) (msecGMT / 1000.0 / 60.0);
            if (minGMT == 0) {
                this.value[9] = 0;
                this.value[10] = 0;
            } else {
                final int hourGMT = (int) (minGMT / 60.0);
                minGMT -= hourGMT * 60;
                this.value[9] = (byte) (hourGMT & 0xFF);
                this.value[10] = (byte) (minGMT & 0xFF);
            }
        }
    }

    public AsnOctets(final InputStream in, final int len) throws IOException {
        this.hash = 0;
        this.value = new byte[len];
        if (len != 0) {
            if (len != in.read(this.value, 0, len)) {
                throw new IOException("AsnOctets(): Not enough data");
            }
            final String str = "";
        }
    }

    public static void setHexPrefix(final String newPrefix) {
        AsnOctets.HEX_PREFIX = newPrefix;
    }

    public static void setPrintable(final AsnOctetsPrintableFace obj) {
        if (obj != null) {
            AsnOctets.printableObject = obj;
        }
    }

    public String getValue() {
        return this.toString();
    }

    public byte[] getBytes() {
        final int len = this.value.length;
        final byte[] bytea = new byte[len];
        System.arraycopy(this.value, 0, bytea, 0, len);
        return bytea;
    }

    @Override
    public String toString() {
        return this.toString(AsnOctets.printableObject);
    }

    public String toString(final AsnOctetsPrintableFace face) {
        String str = "";
        if (this.type == 64) {
            str = this.toIpAddress();
        } else if (this.type == 68) {
            str = AsnOctets.HEX_PREFIX + this.toHex();
        } else {
            final boolean isPrintable = face.isPrintable(this.value);
            if (isPrintable) {
                str = face.toInternationalDisplayString(this.value);
            } else {
                str = AsnOctets.HEX_PREFIX + this.toHex();
            }
        }
        return str;
    }

    @Override
    int size() {
        return this.value.length;
    }

    @Override
    void write(final OutputStream out, final int pos) throws IOException {
        this.AsnBuildHeader(out, this.type, this.value.length);
        if (AsnOctets.debug > 10) {
            System.out.println("\tAsnOctets(): value = " + this.toString() + ", pos = " + pos);
        }
        for (int idx = 0; idx < this.value.length; ++idx) {
            out.write(this.value[idx]);
        }
    }

    public String toIpAddress() {
        final StringBuffer sb = new StringBuffer(39);
        final int length = this.value.length;
        if (length > 0) {
            if (length > 4) {
                for (int i = 0; i < length / 2; ++i) {
                    sb.append(Integer.toHexString((this.value[i << 1] << 8 & 0xFF00) | (this.value[(i << 1) + 1] & 0xFF)));
                    if (i < length / 2 - 1) {
                        sb.append(":");
                    }
                }
            } else {
                for (int i = 0; i < length - 1; ++i) {
                    final long val = this.getPositiveValue(i);
                    sb.append(String.valueOf(val)).append(".");
                }
                final long val = this.getPositiveValue(length - 1);
                sb.append(String.valueOf(val));
            }
        }
        return sb.toString();
    }

    public InetAddress getIpAddress() throws RuntimeException {
        InetAddress iad = null;
        try {
            iad = InetAddress.getByAddress(this.value);
        } catch (UnknownHostException exc) {
            throw new RuntimeException(exc);
        }
        return iad;
    }

    private long getPositiveValue(final int index) {
        long val = this.value[index];
        if (val < 0L) {
            val += 256L;
        }
        return val;
    }

    public String toHex() {
        final StringBuffer buffer = new StringBuffer("");
        final int length = this.value.length;
        if (length > 0) {
            for (int i = 0; i < length - 1; ++i) {
                buffer.append(SnmpUtilities.toHex(this.value[i])).append(":");
            }
            buffer.append(SnmpUtilities.toHex(this.value[length - 1]));
        }
        return buffer.toString();
    }

    public String toDisplayString() {
        String str = "";
        final int length = this.value.length;
        if (length > 0) {
            try {
                str = new String(this.value, "US-ASCII");
            } catch (UnsupportedEncodingException exc) {
                str = new String(this.value);
            }
            str = str.trim();
        }
        return str;
    }

    public String toInternationalDisplayString() {
        return this.toInternationalDisplayString(AsnOctets.printableObject);
    }

    public String toInternationalDisplayString(final AsnOctetsPrintableFace face) {
        return face.toInternationalDisplayString(this.value);
    }

    public String toCalendar() throws RuntimeException {
        final Calendar cal = this.getCalendar();
        final Date date = cal.getTime();
        return AsnOctets.CALFORMAT.format(date);
    }

    public Calendar getCalendar() throws RuntimeException {
        final Calendar cal = Calendar.getInstance();
        if (this.value.length == 8 || this.value.length == 11) {
            final int year = (int) (this.getPositiveValue(0) * 256L + this.getPositiveValue(1));
            final int month = this.value[2] - 1;
            final int day = this.value[3];
            final int hour = this.value[4];
            final int min = this.value[5];
            final int sec = this.value[6];
            final int msec = this.value[7] * 100;
            cal.set(year, month, day, hour, min, sec);
            cal.set(14, msec);
            if (this.value.length == 11) {
                final char dir = (char) this.value[8];
                final int hourUTC = this.value[9];
                final int minUTC = this.value[10];
                final int secUTC = hourUTC * 60 * 60;
                int msecGMT = secUTC * 1000;
                if (dir == '-') {
                    msecGMT *= -1;
                }
                cal.set(15, msecGMT);
            }
            return cal;
        }
        throw new RuntimeException("AsnOctets is not DateAndTime");
    }

    public long[] toSubOid(final boolean length_implied) {
        int index = 0;
        final int length = this.value.length;
        long[] sub_oid;
        if (length_implied) {
            sub_oid = new long[length];
        } else {
            sub_oid = new long[length + 1];
            sub_oid[0] = length;
            ++index;
        }
        for (int i = 0; i < length; ++i) {
            sub_oid[index] = this.getPositiveValue(i);
            ++index;
        }
        return sub_oid;
    }

    @Override
    public boolean equals(final Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof AsnOctets) {
            final AsnOctets anotherOctet = (AsnOctets) anObject;
            int n = this.value.length;
            if (n == anotherOctet.value.length) {
                final byte[] v1 = this.value;
                final byte[] v2 = anotherOctet.value;
                int i = 0;
                int j = 0;
                while (n-- != 0) {
                    if (v1[i++] != v2[j++]) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        int h = this.hash;
        if (h == 0) {
            int off = 0;
            final byte[] val = this.value;
            for (int len = this.value.length, i = 0; i < len; ++i) {
                h = 31 * h + val[off++];
            }
            this.hash = h;
        }
        return h;
    }

    static {
        AsnOctets.HEX_PREFIX = "0x";
        AsnOctets.CALFORMAT = new SimpleDateFormat("yyyy-M-d,HH:mm:ss.SS,z");
        AsnOctets.printableObject = new DefaultAsnOctetsPrintable();
    }
}
